/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
**   * Redistributions of source code must retain the above copyright
**     notice, this list of conditions and the following disclaimer.
**   * Redistributions in binary form must reproduce the above copyright
**     notice, this list of conditions and the following disclaimer in
**     the documentation and/or other materials provided with the
**     distribution.
**   * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
**     the names of its contributors may be used to endorse or promote
**     products derived from this software without specific prior written
**     permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
** $QT_END_LICENSE$
**
****************************************************************************/

/****************************************************************************
** LICENSE:BSD
** Author: Alejandro Castaño del Castillo
** Author: Francisco Javier Martin Otero
** Author: Fernando Rosario Costa
****************************************************************************/

#define N_ANGLES 12

#define DEFAULT_LENGTH_THRESHOLD 8
#define DEFAULT_SIZE_THRESHOLD 200
#define DEFAULT_BINARY_THRESHOLD 200

#define DEFAULT_MINIMUM_LENGTH_THRESHOLD 0
#define DEFAULT_MAXIMUM_LENGTH_THRESHOLD 15
#define DEFAULT_MINIMUM_SIZE_THRESHOLD 40
#define DEFAULT_MAXIMUM_SIZE_THRESHOLD 600
#define DEFAULT_MINIMUM_BINARY_THRESHOLD 0
#define DEFAULT_MAXIMUM_BINARY_THRESHOLD 255
#define DEFAULT_STEP_THRESHOLD 1

#include <QtGui>

#include "mainwindow.h"

using namespace cv;

MainWindow::MainWindow()
{
    // Set pixmap properties
    imageLabel = new QLabel;
    imageLabel->setBackgroundRole(QPalette::Base);
    imageLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
    imageLabel->setScaledContents(true);

    // Pixmap is in the scroll area
    scrollArea = new QScrollArea;
    scrollArea->setBackgroundRole(QPalette::Dark);
    scrollArea->setWidget(imageLabel);
    // And the scroll area is the center of the window
    setCentralWidget(scrollArea);

    loadDescriptions();

    // Add a slider
    slider = new QSlider(Qt::Horizontal);
    slider->setEnabled(false);
    slider->setMinimum(0);
    slider->setMaximum(descriptions.size() - 1);
    // When the slider changes, description and image change too
    connect(slider, SIGNAL(valueChanged(int)), this, SLOT(slotValueChanged(int)));

    // Slider is on the top of the window
    topDock = new QDockWidget(tr("Steps"));
    topDock->setWidget(slider);
    topDock->setFeatures(QDockWidget::NoDockWidgetFeatures);
    addDockWidget(Qt::TopDockWidgetArea, topDock);

    // Add a text where descriptions are written
    text = new QTextEdit();
    text->setReadOnly(true);
    text->setEnabled(false);

    // Text area in the bottom of the window
    downDock =  new QDockWidget(tr("Description"));
    downDock->setWidget(text);
    downDock->setFeatures(QDockWidget::NoDockWidgetFeatures);
    addDockWidget(Qt::BottomDockWidgetArea, downDock);

    // Right part
    QVBoxLayout* rightLayout = new QVBoxLayout();


    QLabel* label = new QLabel();
    label->setFrameStyle(QFrame::HLine);
    label->setLineWidth(2);
    rightLayout->addWidget(label);

    label = new QLabel();
    label->setText(tr("Binary threshold"));
    rightLayout->addWidget(label);
    inputBinaryThreshold = new QSpinBox();
    inputBinaryThreshold->setMinimum(DEFAULT_MINIMUM_BINARY_THRESHOLD);
    inputBinaryThreshold->setMaximum(DEFAULT_MAXIMUM_BINARY_THRESHOLD);
    inputBinaryThreshold->setValue(DEFAULT_BINARY_THRESHOLD);
    inputBinaryThreshold->setSingleStep(DEFAULT_STEP_THRESHOLD);
    rightLayout->addWidget(inputBinaryThreshold);

    label = new QLabel();
    label->setText(tr("Length threshold"));
    rightLayout->addWidget(label);
    inputLengthThreshold = new QSpinBox();
    inputLengthThreshold->setMinimum(DEFAULT_MINIMUM_LENGTH_THRESHOLD);
    inputLengthThreshold->setMaximum(DEFAULT_MAXIMUM_LENGTH_THRESHOLD);
    inputLengthThreshold->setValue(DEFAULT_LENGTH_THRESHOLD);
    inputLengthThreshold->setSingleStep(DEFAULT_STEP_THRESHOLD);
    rightLayout->addWidget(inputLengthThreshold);

    label = new QLabel();
    label->setText(tr("Component size threshold"));
    rightLayout->addWidget(label);
    inputSizeThreshold = new QSpinBox();
    inputSizeThreshold->setMinimum(DEFAULT_MINIMUM_SIZE_THRESHOLD);
    inputSizeThreshold->setMaximum(DEFAULT_MAXIMUM_SIZE_THRESHOLD);
    inputSizeThreshold->setValue(DEFAULT_SIZE_THRESHOLD);
    inputSizeThreshold->setSingleStep(DEFAULT_STEP_THRESHOLD);
    rightLayout->addWidget(inputSizeThreshold);

    rightLayout->addStretch();

    QPushButton *button = new QPushButton("&Apply changes");
    rightLayout->addWidget(button);

    QWidget* auxWidget = new QWidget();
    auxWidget->setLayout(rightLayout);

    // Right dock
    rightDock = new QDockWidget(tr("Input Values"));
    rightDock->setWidget(auxWidget);
    rightDock->setFeatures(QDockWidget::NoDockWidgetFeatures);
    addDockWidget(Qt::RightDockWidgetArea, rightDock);

    connect(button, SIGNAL(clicked()), this, SLOT(slotApplyChanges()));

    createActions();
    createMenus();

    setWindowTitle(tr("Valley fill"));
    //resize(500, 400);
}

void MainWindow::open()
{
    // Dialog to select the file
    QString fileName = QFileDialog::getOpenFileName(this,
                                    tr("Open File"), QDir::currentPath());
    // If a file was select
    if (!fileName.isEmpty())
    {
        imageName = fileName.toUtf8().constData();
        // But the file wasn't load correctly as image
        if (!loadImages())
        {
            QMessageBox::information(this, tr("Image Viewer"),
                                     tr("Cannot load %1.").arg(fileName));
            return;
        }

        // If not
        // the slider and the text are enabled
        slider->setEnabled(true);
        text->setEnabled(true);

        // And set the slider to 0
        slider->setValue(0);
        slotValueChanged(0);

        scaleFactor = 1.0;

        printAct->setEnabled(true);
        fitToWindowAct->setEnabled(true);
        updateActions();

        if (!fitToWindowAct->isChecked())
            imageLabel->adjustSize();
    }
}

void MainWindow::print()
{
    Q_ASSERT(imageLabel->pixmap());
#ifndef QT_NO_PRINTER
    QPrintDialog dialog(&printer, this);
    if (dialog.exec()) {
        QPainter painter(&printer);
        QRect rect = painter.viewport();
        QSize size = imageLabel->pixmap()->size();
        size.scale(rect.size(), Qt::KeepAspectRatio);
        painter.setViewport(rect.x(), rect.y(), size.width(), size.height());
        painter.setWindow(imageLabel->pixmap()->rect());
        painter.drawPixmap(0, 0, *imageLabel->pixmap());
    }
#endif
}

void MainWindow::zoomIn()
{
    scaleImage(1.25);
}

void MainWindow::zoomOut()
{
    scaleImage(0.8);
}

void MainWindow::normalSize()
{
    imageLabel->adjustSize();
    scaleFactor = 1.0;
}

void MainWindow::fitToWindow()
{
    bool fitToWindow = fitToWindowAct->isChecked();
    scrollArea->setWidgetResizable(fitToWindow);
    if (!fitToWindow) {
        normalSize();
    }
    updateActions();
}

void MainWindow::about()
{
    QMessageBox::about(this, tr("About Image Viewer"),
            tr("<p>This program detects creases in fingerprint images.</p>"));
}

void MainWindow::slotValueChanged(int value)
{
    text->setText(descriptions[value]);
    imageLabel->setPixmap(images[value]);
}

void MainWindow::createActions()
{
    openAct = new QAction(tr("&Open..."), this);
    openAct->setShortcut(tr("Ctrl+O"));
    connect(openAct, SIGNAL(triggered()), this, SLOT(open()));

    printAct = new QAction(tr("&Print..."), this);
    printAct->setShortcut(tr("Ctrl+P"));
    printAct->setEnabled(false);
    connect(printAct, SIGNAL(triggered()), this, SLOT(print()));

    exitAct = new QAction(tr("E&xit"), this);
    exitAct->setShortcut(tr("Ctrl+Q"));
    connect(exitAct, SIGNAL(triggered()), this, SLOT(close()));

    zoomInAct = new QAction(tr("Zoom &In (25%)"), this);
    zoomInAct->setShortcut(tr("Ctrl++"));
    zoomInAct->setEnabled(false);
    connect(zoomInAct, SIGNAL(triggered()), this, SLOT(zoomIn()));

    zoomOutAct = new QAction(tr("Zoom &Out (25%)"), this);
    zoomOutAct->setShortcut(tr("Ctrl+-"));
    zoomOutAct->setEnabled(false);
    connect(zoomOutAct, SIGNAL(triggered()), this, SLOT(zoomOut()));

    normalSizeAct = new QAction(tr("&Normal Size"), this);
    normalSizeAct->setShortcut(tr("Ctrl+S"));
    normalSizeAct->setEnabled(false);
    connect(normalSizeAct, SIGNAL(triggered()), this, SLOT(normalSize()));

    fitToWindowAct = new QAction(tr("&Fit to Window"), this);
    fitToWindowAct->setEnabled(false);
    fitToWindowAct->setCheckable(true);
    fitToWindowAct->setShortcut(tr("Ctrl+F"));
    connect(fitToWindowAct, SIGNAL(triggered()), this, SLOT(fitToWindow()));

    aboutAct = new QAction(tr("&About"), this);
    connect(aboutAct, SIGNAL(triggered()), this, SLOT(about()));

    aboutQtAct = new QAction(tr("About &Qt"), this);
    connect(aboutQtAct, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
}

void MainWindow::createMenus()
{
    fileMenu = new QMenu(tr("&File"), this);
    fileMenu->addAction(openAct);
    fileMenu->addAction(printAct);
    fileMenu->addSeparator();
    fileMenu->addAction(exitAct);

    viewMenu = new QMenu(tr("&View"), this);
    viewMenu->addAction(zoomInAct);
    viewMenu->addAction(zoomOutAct);
    viewMenu->addAction(normalSizeAct);
    viewMenu->addSeparator();
    viewMenu->addAction(fitToWindowAct);

    helpMenu = new QMenu(tr("&Help"), this);
    helpMenu->addAction(aboutAct);
    helpMenu->addAction(aboutQtAct);

    menuBar()->addMenu(fileMenu);
    menuBar()->addMenu(viewMenu);
    menuBar()->addMenu(helpMenu);
}

void MainWindow::updateActions()
{
    zoomInAct->setEnabled(!fitToWindowAct->isChecked());
    zoomOutAct->setEnabled(!fitToWindowAct->isChecked());
    normalSizeAct->setEnabled(!fitToWindowAct->isChecked());
}

void MainWindow::scaleImage(double factor)
{
    Q_ASSERT(imageLabel->pixmap());
    scaleFactor *= factor;
    imageLabel->resize(scaleFactor * imageLabel->pixmap()->size());

    adjustScrollBar(scrollArea->horizontalScrollBar(), factor);
    adjustScrollBar(scrollArea->verticalScrollBar(), factor);

    zoomInAct->setEnabled(scaleFactor < 3.0);
    zoomOutAct->setEnabled(scaleFactor > 0.333);
}

void MainWindow::adjustScrollBar(QScrollBar *scrollBar, double factor)
{
    scrollBar->setValue(int(factor * scrollBar->value()
                            + ((factor - 1) * scrollBar->pageStep()/2)));
}

void MainWindow::loadDescriptions()
{
    descriptions.append("Original image.");
    descriptions.append("Binarization.\n\nThe threshold can be selected on right parameters.");
    descriptions.append("Filling horizontal valleys.\n\nThis step uses creases width property. All white vertical regions between two black pixels that are smaller than the threshold (selectable on right) are filled black.");
    descriptions.append("Filling vertical valleys.\n\nIt's similar to previous step but fills white horizontal regions. The threshold is the same as above.");
    descriptions.append("Calculating fingerprint superior hull.\n\nBrowsing the image by columns (up-down), it paints all white pixel to black until a black pixel is reached.");
    descriptions.append("Calculating fingerprint inferior hull.\n\nIt's the same step as above but browses down-up.");
    descriptions.append("Calculating connected components.\n\nDistinguishes between not connected white regions.");
    descriptions.append("Selecting connected components.\n\nThis step select 'creases' by connected component size. The size threshold can be selected on right.");
    descriptions.append("Showing selected candidates with original image.\n\nOverlaps original image with selected-connected-components image.");
}

void MainWindow::slotApplyChanges()
{
    loadImages();
    slotValueChanged(slider->value());
}

bool MainWindow::loadImages()
{
    if(imageName.isEmpty())
        return false;

    Mat img = imread(imageName.toUtf8().constData(),0);
    if(!img.data)
        return false;

    images.clear();

    // First. the original image
    images.append(fromMatToQPixmap(img));

    Mat binary(img.rows,img.cols,img.type());
    threshold(img, binary, inputBinaryThreshold->value(), 255, THRESH_BINARY);
    images.append(fromMatToQPixmap(binary));
    int length_threshold = inputLengthThreshold->value();
    // Horizontal valley fill
    for(int imgcols = 0 ; imgcols < img.cols ; imgcols++)
    {
        for(int search = 0 ; search < img.rows ; )
        {
            if(binary.at<uchar>(search,imgcols) == 0) // Finded a black pixel
            {
                // Searching next white pixel
                while(search < img.rows && binary.at<uchar>(search,imgcols) == 0) search++;
                // Now we mark the pixel and search for next black pixel.
                // If length to next black pixel is less than threshold
                // this region is painted black.
                int mark = search;
                int length = 0;
                while(search < img.rows && binary.at<uchar>(search,imgcols) != 0)
                {
                    length++;
                    search++;
                }
                // We have white region length. If it is less than threshold, we paint it
                if(length < length_threshold)
                {
                    while(mark != search)
                    {
                        binary.at<uchar>(mark,imgcols) = 0;
                        mark++;
                    }
                }
            }
            else
            {
                search++;
            }
        }
    }
    images.append(fromMatToQPixmap(binary));
    // Vertical vallet fill
    for(int imgrows = 0 ; imgrows < img.rows ; imgrows++)
    {
        for(int search = 0 ; search < img.cols ; )
        {
            if(binary.at<uchar>(imgrows,search) == 0)
            {
                while(search < img.cols && binary.at<uchar>(imgrows,search) == 0) search++;
                int mark = search;
                int length = 0;
                while(search < img.cols && binary.at<uchar>(imgrows,search) != 0)
                {
                    length++;
                    search++;
                }
                if(length < length_threshold)
                {
                    while(mark != search)
                    {
                        binary.at<uchar>(imgrows,mark) = 0;
                        mark++;
                    }
                }
            }
            else
            {
                search++;
            }
        }
    }
    images.append(fromMatToQPixmap(binary));

    // Calculating fingerprint hull.
    // Up-Down
    for(int imgcols = 0 ; imgcols < img.cols ; imgcols++)
    {
        for(int filler = 0 ; filler < img.rows ; filler++)
        {
            if(binary.at<uchar>(filler,imgcols) != 0)
                binary.at<uchar>(filler,imgcols) = 0;
            else
                break;
        }
    }
    images.append(fromMatToQPixmap(binary));
    // Down-Up
    for(int imgcols = 0 ; imgcols < img.cols ; imgcols++)
    {
        for(int filler = img.rows - 1 ; filler >= 0 ; filler--)
        {
            if(binary.at<uchar>(filler,imgcols) != 0)
                binary.at<uchar>(filler,imgcols) = 0;
            else
                break;
        }
    }
    images.append(fromMatToQPixmap(binary));

    Mat connected_components(binary.rows,binary.cols,CV_32S,Scalar(0));
    int tag = 1;
    // First pixel
    if(binary.at<uchar>(0,0) == 255)
    {
        connected_components.at<int>(0,0) = tag;
        tag++;
    }
    // First column
    for(int imgrows = 1 ; imgrows < binary.rows ; imgrows++)
    {
        if(binary.at<uchar>(imgrows,0) == 255)
        {
            if(binary.at<uchar>(imgrows-1,0) == 255)
            {
                connected_components.at<int>(imgrows,0) = connected_components.at<int>(imgrows,0);
            }
            else
            {
                connected_components.at<int>(imgrows,0) = tag;
                tag++;
            }
        }
    }
    // First row
    for(int imgcols = 1 ; imgcols < binary.cols ; imgcols++)
    {
        if(binary.at<uchar>(0,imgcols) == 255)
        {
            if(binary.at<uchar>(0,imgcols-1) == 255)
            {
                connected_components.at<int>(0,imgcols) = connected_components.at<int>(0,imgcols-1);
            }
            else
            {
                connected_components.at<int>(0,imgcols) = tag;
                tag++;
            }
        }
    }
    QVector<QVector<int> > equal_tags; // Tags
    for(int imgrows = 1 ; imgrows < binary.rows ; imgrows++)
    {
        for(int imgcols = 1 ; imgcols < binary.cols ; imgcols++)
        {
            if(binary.at<uchar>(imgrows,imgcols) == 255)
            {
                // Both blacks
                if(binary.at<uchar>(imgrows-1,imgcols) == 0 && binary.at<uchar>(imgrows,imgcols-1) == 0)
                {
                    connected_components.at<int>(imgrows,imgcols) = tag;
                    QVector<int> new_class;
                    new_class.append(tag);
                    equal_tags.append(new_class);
                    tag++;
                }
                // One white
                else if(binary.at<uchar>(imgrows-1,imgcols) == 0 || binary.at<uchar>(imgrows,imgcols-1) == 0)
                {
                    connected_components.at<int>(imgrows,imgcols) = connected_components.at<int>(imgrows-1,imgcols) +
                                                                      connected_components.at<int>(imgrows,imgcols-1);
                }
                // Both white
                else
                {
                    connected_components.at<int>(imgrows,imgcols) = connected_components.at<int>(imgrows-1,imgcols);
                    // Equal tags
                    int left = connected_components.at<int>(imgrows,imgcols-1);
                    int up = connected_components.at<int>(imgrows-1,imgcols);
                    if(left != up)
                    {
                        int find = 0;
                        for(int tag_class = 0 ; tag_class < equal_tags.size() ; tag_class++)
                        {
                            // Group has equivalence class values
                            QVector<int> actual_tag = equal_tags.at(tag_class);
                            if(actual_tag.contains(left))
                            {
                                find = 1;
                                if(!actual_tag.contains(up))
                                {
                                    actual_tag.append(up);
                                    equal_tags.replace(tag_class,actual_tag);
                                }
                                break;
                            }
                            else if(actual_tag.contains(up))
                            {
                                find = 1;
                                actual_tag.append(left);
                                equal_tags.replace(tag_class,actual_tag);
                                break;
                            }
                        }
                        if(!find) // If class is not found, a new one is added
                        {
                            QVector<int> new_class;
                            new_class.append(left);
                            new_class.append(up);
                            equal_tags.append(new_class);
                        }
                        else // Erasing an unitary tag
                        {
                            for(int remove = 0 ; remove < equal_tags.size() ; remove++)
                            {
                                QVector<int> candidate = equal_tags.at(remove);
                                if(candidate.size() == 1 && (candidate.at(0) == left || candidate.at(0) == up))
                                {
                                    equal_tags.remove(remove);
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    // All tags must be equal
    // Calculating connected component size
    // We use coordinates (x,y) from class Point to model
    // x = connected component, y = size
    // Initializing connected component size to 0
    QVector<Point> connected_components_size;
    for(int tag_class = 0 ; tag_class < equal_tags.size() ; tag_class++)
    {
        QVector<int> actual_tag = equal_tags.at(tag_class);
        Point p(actual_tag.at(0),0);
        connected_components_size.append(p);
    }
    for(int imgrows = 0 ; imgrows < connected_components.rows ; imgrows++)
    {
        for(int imgcols = 0 ; imgcols < connected_components.cols ; imgcols++)
        {
            int actual_tag = connected_components.at<int>(imgrows,imgcols);
            for(int tag_class = 0 ; tag_class < equal_tags.size() ; tag_class++)
            {
                QVector<int> actual_class_tag = equal_tags.at(tag_class);
                if(actual_class_tag.contains(actual_tag))
                {
                    int normalized_tag = actual_class_tag.at(0);
                    connected_components.at<int>(imgrows,imgcols) = normalized_tag;
                    for(int search_component = 0 ; search_component < connected_components_size.size() ; search_component++)
                    {
                        if(connected_components_size.at(search_component).x == normalized_tag)
                        {
                            Point new_point(normalized_tag,connected_components_size.at(search_component).y + 1);
                            connected_components_size.replace(search_component,new_point);
                        }
                    }
                }
            }
        }
    }
    images.append(fromMatToQPixmap32(connected_components));

    // Selecting connected components
    Mat final(binary.rows,binary.cols,binary.type());
    int component_size_threshold = inputSizeThreshold->value();
    for(int imgrows = 0 ; imgrows < final.rows ; imgrows++)
    {
        for(int imgcols = 0 ; imgcols < final.cols ; imgcols++)
        {
            int actual_component = connected_components.at<int>(imgrows,imgcols);
            if(actual_component == 0)
            {
                final.at<uchar>(imgrows,imgcols) = 255;
            }
            else
            {
                // Loop that searchs for the corresponding element of
                // connected component size vector
                Point size_container;
                for(int search = 0 ; search < connected_components_size.size() ; search++)
                {
                    size_container = connected_components_size.at(search);
                    if(size_container.x == actual_component)
                        break;
                }
                // Now we have the size_container we want
                // If it is less than threshold, it is painted white
                if(size_container.y < component_size_threshold)
                {
                    final.at<uchar>(imgrows,imgcols) = 255;
                }
                // If it is greater than threshold, it is painted black
                else
                {
                    final.at<uchar>(imgrows,imgcols) = 0;
                }
            }
        }
    }
    images.append(fromMatToQPixmap(final));

    Mat overlap(img.rows,img.cols,img.type());
    for(int imgrows = 0 ; imgrows < overlap.rows ; imgrows++)
    {
        for(int imgcols = 0 ; imgcols < overlap.cols ; imgcols++)
        {
            if(final.at<uchar>(imgrows,imgcols) == 0)
                overlap.at<uchar>(imgrows,imgcols) = 128;
            else
                overlap.at<uchar>(imgrows,imgcols) = img.at<uchar>(imgrows,imgcols);
        }
    }
    images.append(fromMatToQPixmap(overlap));

    return true;
}

const QPixmap MainWindow::fromMatToQPixmap(const Mat& m)
{
    QImage qimage((unsigned char *)m.datastart, m.cols, m.rows, m.step, QImage::Format_Indexed8);
    for(int i = 0 ; i < 256 ; i++)
        qimage.setColor(i,qRgb(i,i,i));

    return QPixmap::fromImage(qimage);
}

const QPixmap MainWindow::fromMatToQPixmap32(const Mat& m)
{
    QImage qimage((unsigned char *)m.datastart, m.cols, m.rows, m.step, QImage::Format_RGB32);

    return QPixmap::fromImage(qimage);
}
